import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_architecture/config/app_config.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:connectivity/connectivity.dart';
import 'package:flutter_architecture/models/result_model.dart';
import 'package:flutter_architecture/utils/log_util.dart';
import 'package:flutter_architecture/utils/shared_preferences_util.dart';
import 'package:flutter_architecture/utils/string_util.dart';
import 'package:logger/logger.dart';
import 'package:dio_log/dio_log.dart';
import 'package:fluttertoast/fluttertoast.dart';
/// 网络请求工具类
/// @author: dingwen
/// @date: 2020/12/30

typedef Success = void Function(dynamic json);
typedef Fail = void Function(String reason, int code);
typedef After = void Function();

class Http {
  ///实例
  static Http _http;

  /// http 请求方法
  static const String GET = 'GET';
  static const String POST = 'POST';
  static const String PUT = 'PUT';
  static const String PATCH = 'PATCH';
  static const String DELETE = 'DELETE';

  /// dio实例
  Dio _dio;

  ///基础选项设置
  BaseOptions _options;

  int connectTimeout = 6000;
  int receiveTimeout = 6000;
  String contentType = Headers.jsonContentType;
  ResponseType responseType = ResponseType.json;
  Object headers = {"Access-Control-Allow-Origin": "*", "X-Client-Type": "mApproval"};

  ///用于判断是否是401错误调转登录页面
  static String _uri = '';

  ///返回Http实例
  static Http getInstance() {
    if (_http == null) {
      _http = Http();
    }
    return _http;
  }

  ///构造器
  Http() {
    init();
  }

  ///初始化
  init() {
    _options = BaseOptions(
        baseUrl: AppConfig.mainServerUrl,
        connectTimeout: connectTimeout,
        receiveTimeout: receiveTimeout,
        contentType: contentType,
        responseType: responseType,
        headers: headers);
    _dio = Dio(_options);

    //添加拦截器配置
    _dio.interceptors.addAll(_initInterceptors());
  }

  ///初始化拦截器配置
  List<Interceptor> _initInterceptors() {
    List<Interceptor> interceptors = [];
    //开启请求日志
    interceptors.add(DioLogInterceptor());
    // 开启cookie
    interceptors.add(CookieManager(CookieJar()));
    interceptors.add(_initInterceptorsWrapper());
    return interceptors;
  }

  ///init InterceptorsWrapper
  Interceptor _initInterceptorsWrapper() {
    return InterceptorsWrapper(
        onRequest: (RequestOptions request) async {
          judgeConnectivityResult(request);
          //如果是登录请求，加上请求头
          if (AppConfig.authList.contains(request?.path)) {
            // login
            request.headers["Authorization"] =
                'Mobile ' + StringUtil.encodeBase64('mobile:mobile');
            // 去掉验证码
            request.headers["X-DisableVerifyCode"] = true;
            log(level: Level.info, fileName: 'http_util.dart', functionName: '_initInterceptorsWrapper', message: '登录请求头：${request.headers.toString()}');
          } else if (!AppConfig.whiteList.contains(request?.path)) {
            // 获取token
//            String token = await getToken();
            String token = '194ac0c4-e321-4213-81f8-9deca7c28cd4';
            if (token == null) {
              Fluttertoast.showToast(
                msg: "未登录!", );
              AppConfig.globalRouteKey.currentState
                  .pushNamedAndRemoveUntil('/login', (route) => false);
              log(level: Level.info, fileName: 'http_util.dart', functionName: '_initInterceptorsWrapper', message: '未登录');
              throw DioError(
                  request: request,
                  type: DioErrorType.RESPONSE,
                  response: Response(statusCode: 401, data: {
                    "message": "未登录！",
                    "code": 401,
                    "path": request.path
                  }));
            }
            request.headers["Authorization"] = 'Bearer ' + token;
            log(level: Level.info, fileName: 'http_util.dart', functionName: '_initInterceptorsWrapper', message: 'token: Bearer $token');
          }
        },
        onResponse: (Response response) => responseHandle(response),
        onError: (DioError error) => errorHandle(error));
  }

  ///判断网络状态
  judgeConnectivityResult(request) async {
    ConnectivityResult connectivityResult =
        await (Connectivity().checkConnectivity());
    switch (connectivityResult) {
      case ConnectivityResult.none:
        log(
            level: Level.error,
            fileName: 'http_util.dart',
            functionName: 'judgeConnectivityResult',
            message: '网络连接异常');
        throw DioError(
            request: request,
            type: DioErrorType.RESPONSE,
            response: Response(statusCode: 504, data: {
              "message": "网络异常，请检查网络链接！",
              "code": 504,
              "path": request.path
            }));
        break;
      case ConnectivityResult.mobile:
        log(level: Level.info, fileName: 'http_util.dart', functionName: 'judgeConnectivityResult', message: '正在使用移动数据');
        break;
      case ConnectivityResult.wifi:
        log(level: Level.error, fileName: 'http_util.dart', functionName: 'judgeConnectivityResult', message: '正在使用wifi');
        break;
    }
  }

  ///获取token
  Future<String> getToken() => SPUtil.getString('access_token');

  ///删除token
  static void deleteToken() => SPUtil.remove('access_token');

  ///响应数据处理
  responseHandle(Response response) {
    response == null
        ? ResultModel(code: response.statusCode)
        : response.data = ResultModel.toModel(response);
    return response;
  }

  ///响应错误处理
  errorHandle(DioError error) {
    ResultModel result;
    if (error?.response?.data != null) {
      result = ResultModel.toModel(error.response);
      if (result.code == 200) {
        result.code = error.response.statusCode;
      }
    } else {
      result = ResultModel(code: 500, data: {});
      error.response = Response(statusCode: 500);
    }

    switch (error.type) {
      case DioErrorType.CONNECT_TIMEOUT:
        result.message = "连接超时！";
        break;
      case DioErrorType.SEND_TIMEOUT:
        result.message = "请求超时！";
        break;
      case DioErrorType.RECEIVE_TIMEOUT:
        result.message = "响应超时！";
        break;
      case DioErrorType.CANCEL:
        result.message = "请求取消！";
        break;
      case DioErrorType.RESPONSE:
        result.message = error.response.data["message"] ?? "请求异常，请联系管理员！";
        break;
      default:
        result.message = "未知错误！";
        break;
    }
    error.response.data = result;
    //token过期重新登录获取token
    if (!AppConfig.authList.contains(_uri) &&
        error.response.statusCode == 401) {
      deleteToken();
      AppConfig.globalRouteKey.currentState
          .pushNamedAndRemoveUntil('/login', (route) => false);
    }
    log(level: Level.error, fileName: 'http_util.dart', functionName: 'errorHandle', message: '$error');
    return error;
  }

  ///请求
  static Future<void> request(String uri,
      {data,
      method = Http.GET,
      contentType = Headers.jsonContentType,
      queryParams,
      headers,
      Success success,
      Fail fail,
      After after}) {
    ResultModel result;
    // 请求参数处理
    queryParams?.forEach((key, value) {
      if (uri.indexOf(key) != -1) {
        uri = uri.replaceAll(':$key', value.toString());
      }
    });
    String url = uri;
    _uri = uri;

    log(level: Level.info, fileName: 'Http请求地址', functionName: 'http', message: '$method $url');
    log(level: Level.info, fileName: 'Http请求体', functionName: 'http', message: '$data');
    log(level: Level.info, fileName: 'Http请求参数', functionName: 'http', message: '$queryParams');

    Options options = Options(method: method, contentType: contentType);
    if (headers != null) {
      options.headers = headers;
    }

    getInstance()
        ._dio
        .request(url, data: data, options: options)
        .then((response) {
      result = response.data;
      if (response.statusCode == 200) {
        result = response.data;
        if (success != null) {
          success(result);
          log(level: Level.info, fileName: 'Http请求响应', functionName: 'http', message: '$result');
        }
      } else {
        if (fail != null) {
          fail(response.statusMessage, response.statusCode);
          log(level: Level.error, fileName: 'http_util.dart', functionName: 'request', message: '${response.statusMessage}\t${response.statusCode}');
        }
      }
      if (after != null) {
        after();
      }
    }).catchError((onError) {
      fail(onError.toString(), 500);
      log(level: Level.error, fileName: 'http_util.dart', functionName: 'request', message: '$onError');
    });

    return Future.value();
  }
}
